#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/**
  * @brief	Estrutura de dados para FIFO
  */
typedef struct
{
	char *buf;	/*!< Ponteiro p/ o buffer da FIFO */
    int head;	/*!< Cabeça da FIFO */
    int tail;	/*!< Cauda da FIFO */
    int size;	/*!< Tamanho da FIFO */
}FIFO_t;

/**
  * @brief	Enumerador de estados da FIFO
  */
typedef enum
{
	FIFO_ISEMPTY = 0,	/*!< FIFO vazia */
	FIFO_NOEMPTY = 1,	/*!< FIFO não está vazia */
	FIFO_ISFULL = 2,	/*!< FIFO cheia */
	FIFO_NOFULL = 3		/*!< FIFO não está cheia */
}FIFOStatus_t;

/**
 * @brief  Inicializa a estrutura da FIFO.
 * @param  FIFO_t: Ponteiro para a estrutura FIFO
 * @param  buf: Ponteiro para o buffer da FIFO
 * @param  size: Tamanho da FIFO
 * @retval Nenhum
 */
void fifo_init(FIFO_t *f, char * buf, int size)
{
	f->head = 0;
    f->tail = 0;
    f->size = size;
    f->buf = buf;
}

/**
 * @brief  Verifica se a FIFO está vazia.
 * @param  FIFO_t: Ponteiro para a estrutura FIFO
 * @retval FifoStatus_t: Retorna FifoStatus_t
 */
FIFOStatus_t fifo_isempty(FIFO_t * f)
{
	if(f->head == f->tail)
		return FIFO_ISEMPTY;
	else
		return FIFO_NOEMPTY;
}

/**
 * @brief  Lê da FIFO.
 * @param  FIFO_t: Ponteiro para a estrutura FIFO
 * @param  buf: Ponteiro para o buffer que irá contem
 * 				os dados lidos
 * @param  nbytes: Número de dados a lêr
 * @retval int: Número de dados lidos
 */
int fifo_read(FIFO_t *f, void *buf, int nbytes)
{
	int i;
    char * p;
    p = buf;

    for(i=0; i < nbytes; i++)
    {
    	/* Verifica se não está vazio */
    	if( f->tail != f->head )
    	{
    		*p++ = f->buf[f->tail];
            f->tail++;
            if( f->tail == f->size )
            {
            	f->tail = 0;
            }
        }
    	else
    		return i;
    }

    return nbytes;
}

/**
 * @brief  Escreve na FIFO.
 * @param  FIFO_t: Ponteiro para a estrutura FIFO
 * @param  buf: Ponteiro para o buffer que contem
 * 				os dados a serem escritos
 * @param  nbytes: Número de dados a escrever
 * @retval int: Número de dados escritos
 */
int fifo_write(FIFO_t *f, const void *buf, int nbytes)
{
	int i;
    const char * p;
    p = buf;

    for(i=0; i < nbytes; i++)
    {
    	/* Verifica se não está cheio */
        if( (f->head + 1 == f->tail) || ( (f->head + 1 == f->size) && (f->tail == 0) ))
        	return i; //no more room
        else
        {
        	f->buf[f->head] = *p++;
            f->head++;  //increment the head
            if(f->head == f->size)
            {
            	f->head = 0;
            }
        }
    }

    return nbytes;
}

static FIFO_t fifo;

char buf[10];

int main()
{
	int opt, n;
	char temp_buf[10];

	fifo_init(&fifo, buf, 10);

	while(1)
	{
		printf("\n[1] Insere, [2] Le, [3] IsEmpty: ");
		scanf("%d", &opt);

		switch(opt)
		{
			case 1:
				bzero(temp_buf, sizeof(temp_buf));
				printf("\nDigite o dado: ");
				scanf("%s", temp_buf);
				printf("Escrito %d dados!\n", fifo_write(&fifo, temp_buf, strlen(temp_buf)));
				break;
			case 2:
				bzero(temp_buf, sizeof(temp_buf));
				printf("\nDigite quantos dados: ");
				scanf("%d", &n);
				printf("\nLido %d dados!\n", fifo_read(&fifo, temp_buf, n));
				printf("Sao eles: %s\n", temp_buf);
				break;
			case 3:
				if(fifo_isempty(&fifo) == FIFO_ISEMPTY)
					printf("\nFIFO Vazia\n");
				else
					printf("\nFIFO nao esta vazia\n");
				break;
			default:
				break;
		}
	}

	return 0;
}






